package com.basker.pisces.domain.command;

import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;

import org.springframework.util.Assert;
import org.springframework.util.StringUtils;

import com.basker.pisces.core.exception.StaleObjectException;
import com.basker.pisces.domain.command.exception.CommandExecuteInvalidObjectException;
import com.basker.pisces.domain.command.exception.CommandExecuteRuntimeException;
import com.basker.pisces.domain.command.run.CommandExecutor;
import com.basker.pisces.domain.constant.DomainConstants;
import com.basker.pisces.domain.validation.IValidationObjectError;
import com.basker.pisces.domain.validation.ValidationErrorsUtils;
import com.basker.pisces.service.exception.IObjectErrorsWrapper.ObjectError;
import com.basker.pisces.service.exception.IServiceErrorResult;
import com.basker.pisces.utils.ExceptionUtils;

/**
 * 命令执行的结果,关键属性有：status、message、data、cause。一般情况下，命令的执行过程中如发生异常会被捕捉并封装在当前类中，调用者可以通过当前对象进行下一步控制。
 *
 * @author hangwen
 */
public class CommandResult {

    /**
     * 正确的结果状态
     *
     * @see CommandResult#getStatus()
     */
    public static final int OK_RESULT = 1;

    /**
     * 一般的错误状态值
     *
     * @see CommandResult#getStatus()
     */
    public static final int ERROR_RESULT = -1;

    /**
     * 默认的成功消息
     *
     * @see #getMessage()
     */
    private static final String SUCCESS_MESSAGE = "success";

    /**
     * 命令在执行过程中出现CommandExecuteRuntimeException时，调用此方法将其封装
     *
     * @param exception
     * @return
     * @see CommandExecutor
     */
    public static CommandResult error(CommandExecuteRuntimeException exception) {
        CommandResult result = new CommandResult(ERROR_RESULT, exception.getMessage());
        result.setCause(exception);

        return result;
    }

    /**
     * 构建一个表示命令执行成功的结果
     *
     * @param data 返回的业务对象
     * @return
     */
    public static CommandResult ok(Object data) {
        return ok(null, data);
    }

    /**
     * 构建一个表示命令执行成功的结果
     *
     * @param message 返回的消息
     * @param data    返回的业务对象
     * @return
     */
    public static CommandResult ok(String message, Object data) {
        if (StringUtils.isEmpty(message)) {
            message = CommandResult.SUCCESS_MESSAGE;
        }

        CommandResult result = new CommandResult(OK_RESULT, message);
        result.setData(data);

        return result;
    }

    private CommandExecuteRuntimeException cause;
    private Object data;
    private int status = 0;
    private String message;

    private CommandResult(int status, String message) {
        this.status = status;
        this.message = message;
    }

    /**
     * 是否因为指定的errorCode而出错
     *
     * @param errorCode
     * @return
     */
    public boolean causeByErrorCode(String errorCode) {
        return this.getObjectError(errorCode) != null;
    }

    /**
     * 是否是因为指定的异常而失败
     *
     * @param exceptionType
     * @return
     */
    public boolean causeByException(Class<? extends Exception> exceptionType) {
        Assert.notNull(exceptionType, "parameter 'exceptionType' is required");

        CommandExecuteRuntimeException cause = this.getCause();
        if (cause == null) {
            return false;
        }

        return ExceptionUtils.causeBy(cause, exceptionType);
    }

    /**
     * 是否获取锁失败
     *
     * @return
     */
    public boolean causeByLockFailure() {
        return this.causeByErrorCode(DomainConstants.ObjectErrorCode.OBJECT_COMMAND_LOCK_FAILURE);
    }

    /**
     * 是否因为违反必填约束而失败
     *
     * @return
     */
    public boolean causeByRequiredViolation() {
        return this.causeByErrorCode(DomainConstants.ObjectErrorCode.REQUIRED_VIOLATION);
    }

    /**
     * 是否是因为执行对象已过期而失败（即违反乐观锁约束）
     *
     * @return
     */
    public boolean causeByStaleObject() {
        return this.causeByException(StaleObjectException.class);
    }

    /**
     * 是否因为违反唯一约束而失败
     *
     * @return
     */
    public boolean causeByUniqueViolation() {
        return this.causeByErrorCode(DomainConstants.ObjectErrorCode.UNIQUE_EXIST_VIOLATION);
    }

    /**
     * 错误原因是否是未通过验证
     *
     * @return
     */
    public boolean causeByValidateFailure() {
        return !this.isSuccess() && this.cause instanceof CommandExecuteInvalidObjectException;
    }

    /**
     * 是否因为违反合法状态而失败
     *
     * @return
     */
    public boolean causeByValidStatusViolation() {
        return this.causeByErrorCode(DomainConstants.ObjectErrorCode.VALID_STATUS_VIOLATION);
    }

    /**
     * 获取导致命令执行失败的异常，如果当前结果本身没有错误，会抛出{@link IllegalStateException}
     *
     * @return
     */
    public CommandExecuteRuntimeException getCause() {
        this.assertError();

        return cause;
    }

    /**
     * 获取命令成功执行后返回的业务对象
     *
     * @return
     */
    public Object getData() {
        return data;
    }

    /**
     * 如果data和传入的类型匹配，则转换返回，否则返回null
     *
     * @param <T>
     * @param dataType
     * @return
     */
    @SuppressWarnings("unchecked")
    public <T> T getData(Class<T> dataType) {
        Assert.notNull(dataType, "parameter 'dataType' is required");
        if (data != null && dataType.isAssignableFrom(data.getClass())) {
            return (T) data;
        }

        return null;
    }

    /**
     * 获取有错误的业务对象
     *
     * @return,只有{@link #causeByValidateFailure()}为true时才会返回错误对象,否则返回null
     */
    public List<Object> getErrorObjects() {
        if (causeByValidateFailure()) {
            return ValidationErrorsUtils.getErrorObjects(this.getErrors());
        }

        return null;
    }

    /**
     * 获取导致错误的对象,只有{@link #causeByValidateFailure()}为true时才会返回错误对象,否则返回null
     *
     * @return
     */
    public List<ObjectError> getErrors() {
        if (causeByValidateFailure()) {
            return ((CommandExecuteInvalidObjectException) this.cause).getErrors();
        }

        return null;
    }

    /**
     * 获取命令成功执行后返回的消息
     *
     * @return
     */
    public String getMessage() {
        return message;
    }

    /**
     * 获取指定的ObjectError，如果相同errorCode有多个返回第一个
     *
     * @param errorCode
     * @return
     */
    public ObjectError getObjectError(String errorCode) {
        if (!this.causeByValidateFailure()) {
            return null;
        }

        Assert.hasText(errorCode, "parameter 'errorCode' is required");

        for (ObjectError objectError : this.getErrors()) {
            if (errorCode.equals(objectError.getErrorCode())) {
                return objectError;
            }
        }

        return null;
    }

    /**
     * 获取指定的ObjectErrors
     *
     * @param errorCode
     * @return
     */
    public List<ObjectError> getObjectErrors(String errorCode) {
        if (!this.causeByValidateFailure()) {
            return Collections.emptyList();
        }

        Assert.hasText(errorCode, "parameter 'errorCode' is required");

        return this.getErrors().stream().filter(objectError -> errorCode.equals(objectError.getErrorCode()))
                .collect(Collectors.toList());

    }

    /**
     * 获取命令执行后的状态，{@link #OK_RESULT}为正确
     *
     * @return
     */
    public int getStatus() {
        return status;
    }

    /**
     * 获取所有IValidationObjectError类型的错误，只有{@link #causeByValidateFailure()}为true时才会返回错误对象,否则返回null
     *
     * @return
     */
    public List<IValidationObjectError<?>> getValidationErrors() {
        if (causeByValidateFailure()) {
            List<ObjectError> errors = this.getErrors();
            return errors.stream().filter(e -> e instanceof IValidationObjectError<?>)
                    .map(e -> (IValidationObjectError<?>) e).collect(Collectors.toList());
        }

        return null;
    }

    /**
     * 命令是否成功执行
     *
     * @return
     */
    public boolean isSuccess() {
        return this.status == OK_RESULT;
    }

    public void setCause(CommandExecuteRuntimeException cause) {
        this.cause = cause;

        if (cause instanceof IServiceErrorResult) {
            this.setStatus(((IServiceErrorResult) cause).getStatus());
        }
    }

    public void setData(Object data) {
        this.data = data;
    }

    public void setMessage(String message) {
        this.message = message;
    }

    public void setStatus(int status) {
        this.status = status;
    }

    /**
     * 尝试从当前的异常对象中获取匹配类型的异常对象，如果找不到则返回根异常。如果当前结果本身没有错误，会抛出{@link IllegalStateException}
     *
     * @param type
     * @return
     */
    public RuntimeException tryGetCause(final Class<? extends RuntimeException> type) {
        this.assertError();

        RuntimeException found = ExceptionUtils.findCause(this.cause, type);
        return found != null ? found : this.cause;
    }

    private void assertError() {
        Assert.state(!this.isSuccess() && this.cause != null, "command result is success!");
    }
}
